package com.iflytek.speech.sample;

import android.media.AudioFormat;
import android.media.AudioTrack;
import android.os.Process;
import android.util.Log;

import com.iflytek.speech.ISSErrors;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * TTS播放器示例代码，初步集成了TTSEngine
 */
public class TtsPlayer implements TTSEngine.ITTSListener {
    // Player
    private final String TAG = "TtsPlayer";

    //audiotrack 参数
    private final static int MSAMPLERATEINHZ = 16000;
    private final static int MCHANNELCONFIG = AudioFormat.CHANNEL_OUT_MONO;
    private final static int MAUDIOFORMAT = AudioFormat.ENCODING_PCM_16BIT;
    private Lock mAudioTrackLock = new ReentrantLock();
    private AudioTrack mAudioTrack = null;
    private int mMinBufferSizeInBytes = 0;

    // Tts
    private ITtsStateListener listener = null;
    private int nPreTextIndex = -1;
    private final AtomicBoolean mOnDataReadyFlag = new AtomicBoolean(false);

    private final static Object LOCK = new Object();

    // 合成工作线程WorkingThread
    private final AtomicInteger mAudioTrackSteamState = new AtomicInteger(AudioTrackSteamState.STREAM_STOPPED);
    private final Object mWorkingThreadSyncObj = new Object();
    private AudioWriteWorkingFunc mAudioWriteWorkingFunc = new AudioWriteWorkingFunc();
    private Thread mThreadAudioWrite = null;
    private final static long WAIT_LOCK_TIME = 4000L;
    // 和TtsPlayer绑定的音频通道
    private int streamType = 3;
    private final String TAG_AUDIOTRACK = streamType + "tag_audiotrack";

    private final TTSEngine TTSENGINE;

    private class AudioTrackSteamState {
        final static int STREAM_STOPPED = 0;
        final static int STREAM_RUNNING = 1;
        final static int STREAM_PAUSED = 2;
        final static int STREAM_RELEASED = 3;
    }

    public TtsPlayer() {
        TTSENGINE = new TTSEngine();
    }

    public void setListener(ITtsStateListener listener) {
        this.listener = listener;
    }

    public int init(String resDir) {
        Log.d(TAG, "Init");
        synchronized (LOCK) {
            try {
                // Create Tts Engine Obj
                int lib_ret = TTSENGINE.init(resDir);
                if (lib_ret != ISSErrors.ISS_SUCCESS) {
                    return lib_ret;
                }

                // Create AudioTrack Obj
                mMinBufferSizeInBytes = AudioTrack.getMinBufferSize(MSAMPLERATEINHZ, MCHANNELCONFIG, MAUDIOFORMAT);
                Log.d(TAG, "mMinBufferSizeInBytes=" + mMinBufferSizeInBytes + ".");
                if (mMinBufferSizeInBytes <= 0) {
                    Log.e(TAG, "Error: AudioTrack.getMinBufferSize("
                            + MSAMPLERATEINHZ + ", " + MCHANNELCONFIG + ", "
                            + MAUDIOFORMAT + ") ret " + mMinBufferSizeInBytes);
                    return ISSErrors.ISS_ERROR_INVALID_PARA;
                }
                if (mAudioTrack == null) {
                    mAudioTrack = new AudioTrack(streamType, MSAMPLERATEINHZ, MCHANNELCONFIG, MAUDIOFORMAT,
                            mMinBufferSizeInBytes * 3, AudioTrack.MODE_STREAM);
                    if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
                        Log.e(TAG, "Error: Can't init AudioRecord!");
                        return ISSErrors.ISS_ERROR_FAIL;
                    }
                    Log.d(TAG, "new AudioTrack(streamType=" + streamType + ")");
                }

                // Start Work Thread
                mAudioTrackSteamState.set(AudioTrackSteamState.STREAM_STOPPED);
                if (mThreadAudioWrite == null) {
                    Log.d(TAG, "mThreadAudioWrite run");
                    mAudioWriteWorkingFunc.clearExitFlag();
                    mThreadAudioWrite = new Thread(mAudioWriteWorkingFunc,
                            "mThreadAudioWrite");
                    mThreadAudioWrite.start();
                }
            } catch (IllegalArgumentException e) {
                return ISSErrors.ISS_ERROR_INVALID_PARA;
            }
            return ISSErrors.ISS_SUCCESS;
        }
    }

    public int setParam(int nParam, int nValue) {
        Log.d(TAG, "SetParam:" + nParam + "=" + nValue);
        return TTSENGINE.setParam(nParam, nValue);
    }

    public int setParamEx(int nParam, String nValue){
        Log.d(TAG, "SetParamEx:" + nParam + "=" + nValue);
        return TTSENGINE.setParamEx(nParam, nValue);
    }

    public int start(String text) {
        Log.d(TAG, "Start");
        if (mAudioTrack == null || mThreadAudioWrite == null) {
            return ISSErrors.ISS_ERROR_INVALID_CALL;
        }
        TTSENGINE.setListener(this);
        stop();
        if (mAudioTrackSteamState.get() == AudioTrackSteamState.STREAM_RELEASED) {
            return ISSErrors.ISS_ERROR_INVALID_CALL;
        } else {
            // Tts
            nPreTextIndex = -1;
            mOnDataReadyFlag.set(false);
            Log.d(TAG, "start text : " + text);
            int ret = TTSENGINE.start(text);
            Log.d(TAG, "TTSENGINE start ret : " + ret);
            if (ret != ISSErrors.ISS_SUCCESS) {
                return ret;
            }
            // Player
            mAudioTrackSteamState.set(AudioTrackSteamState.STREAM_RUNNING);
            boolean lockResult = false;
            try {
                lockResult = mAudioTrackLock.tryLock(WAIT_LOCK_TIME, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                Log.e(TAG_AUDIOTRACK, "try lock audio track fail,err " + e.getMessage());
            }
            if (!lockResult) {
                int resetResult = resetAudioTrack();
                if (resetResult != 0) {
                    Log.e(TAG_AUDIOTRACK, "reset audio track err");
                    return -1;
                }
                mAudioTrackLock.lock();
            }
            Log.d(TAG, "mAudioTrack start play: ");
            if (mAudioTrack != null) {
                mAudioTrack.play();
            }
            Log.d(TAG, "mAudioTrack end play: ");
            mAudioTrackLock.unlock();
            synchronized (mWorkingThreadSyncObj) {
                mWorkingThreadSyncObj.notifyAll();
            }
            return ISSErrors.ISS_SUCCESS;
        }
    }

    /**
     * 重置AudioTrack
     */
    private int resetAudioTrack() {
        // 先强制抛出onComplete回调
        if (listener != null) {
            Log.d(TAG_AUDIOTRACK, "audio track block,force complete");
            listener.onTTSPlayCompleted();
        }
        // 前一个线程退出，同时会将之前的AudioTrack
        mAudioWriteWorkingFunc.setExitFlag();
        // 重新创建同步锁
        mAudioTrackLock = new ReentrantLock();
        // 重新创建AudioTrack
        mMinBufferSizeInBytes = AudioTrack.getMinBufferSize(MSAMPLERATEINHZ, MCHANNELCONFIG, MAUDIOFORMAT);
        Log.d(TAG, "mMinBufferSizeInBytes=" + mMinBufferSizeInBytes + ".");
        if (mMinBufferSizeInBytes <= 0) {
            Log.e(TAG_AUDIOTRACK, "Error: AudioTrack.getMinBufferSize(" + MSAMPLERATEINHZ + ", " + MCHANNELCONFIG + ", "
                    + MAUDIOFORMAT + ") ret " + mMinBufferSizeInBytes);
            return -1;
        }
        mAudioTrack = new AudioTrack(streamType, MSAMPLERATEINHZ, MCHANNELCONFIG, MAUDIOFORMAT,
                mMinBufferSizeInBytes * 3, AudioTrack.MODE_STREAM);
        if (mAudioTrack == null || mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
            Log.e(TAG_AUDIOTRACK, "Error: Can't init AudioRecord!");
            return -1;
        }
        // Start Work Thread
        mAudioTrackSteamState.set(AudioTrackSteamState.STREAM_STOPPED);
        mAudioWriteWorkingFunc = new AudioWriteWorkingFunc();
        mThreadAudioWrite = new Thread(mAudioWriteWorkingFunc, "reset_tts_" + streamType);
        mThreadAudioWrite.start();
        return 0;
    }

    public int pause() {
        Log.d(TAG, "Pause");
        if (mAudioTrack == null || mThreadAudioWrite == null) {
            return ISSErrors.ISS_ERROR_INVALID_CALL;
        }
        if (mAudioTrackSteamState.get() == AudioTrackSteamState.STREAM_RELEASED) {
            return ISSErrors.ISS_ERROR_INVALID_CALL;
        } else if (mAudioTrackSteamState.get() == AudioTrackSteamState.STREAM_STOPPED) {
            return ISSErrors.ISS_ERROR_INVALID_CALL;
        } else if (mAudioTrackSteamState.get() == AudioTrackSteamState.STREAM_PAUSED) {
            return ISSErrors.ISS_SUCCESS;
        } else {
            mAudioTrackSteamState.set(AudioTrackSteamState.STREAM_PAUSED);
            boolean lockResult = false;
            try {
                lockResult = mAudioTrackLock.tryLock(WAIT_LOCK_TIME, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                Log.e(TAG_AUDIOTRACK, "try lock audio track fail,err " + e.getMessage());
            }
            if (!lockResult) {
                int resetResult = resetAudioTrack();
                if (resetResult != 0) {
                    Log.e(TAG_AUDIOTRACK, "reset audio track err");
                    return -1;
                }
                mAudioTrackLock.lock();
            }
            if (mAudioTrack != null) {
                mAudioTrack.pause();
            }
            mAudioTrackLock.unlock();
            synchronized (mWorkingThreadSyncObj) {
                mWorkingThreadSyncObj.notifyAll();
            }
            return ISSErrors.ISS_SUCCESS;
        }
    }

    public int resume() {
        Log.d(TAG, "Resume");
        if (mAudioTrack == null || mThreadAudioWrite == null) {
            return ISSErrors.ISS_ERROR_INVALID_CALL;
        }
        if (mAudioTrackSteamState.get() == AudioTrackSteamState.STREAM_RELEASED) {
            return ISSErrors.ISS_ERROR_INVALID_CALL;
        } else if (mAudioTrackSteamState.get() == AudioTrackSteamState.STREAM_STOPPED) {
            return ISSErrors.ISS_ERROR_INVALID_CALL;
        } else if (mAudioTrackSteamState.get() == AudioTrackSteamState.STREAM_RUNNING) {
            return ISSErrors.ISS_SUCCESS;
        } else {
            mAudioTrackSteamState.set(AudioTrackSteamState.STREAM_RUNNING);
            boolean lockResult = false;
            try {
                lockResult = mAudioTrackLock.tryLock(WAIT_LOCK_TIME, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                Log.e(TAG_AUDIOTRACK, "try lock audio track fail,err " + e.getMessage());
            }
            if (!lockResult) {
                int resetResult = resetAudioTrack();
                if (resetResult != 0) {
                    Log.e(TAG_AUDIOTRACK, "reset audio track err");
                    return -1;
                }
                mAudioTrackLock.lock();
            }
            if (mAudioTrack != null) {
                mAudioTrack.play();
            }
            mAudioTrackLock.unlock();
            synchronized (mWorkingThreadSyncObj) {
                mWorkingThreadSyncObj.notifyAll();
            }
            return ISSErrors.ISS_SUCCESS;
        }
    }

    public int stop() {
        Log.d(TAG, "Stop");
        if (mAudioTrack == null || mThreadAudioWrite == null) {
            return ISSErrors.ISS_ERROR_INVALID_CALL;
        }
        Log.d(TAG, "mAudioTrackSteamState:" + mAudioTrackSteamState.get());
        if (mAudioTrackSteamState.get() == AudioTrackSteamState.STREAM_RELEASED) {
            return ISSErrors.ISS_ERROR_INVALID_CALL;
        } else if (mAudioTrackSteamState.get() == AudioTrackSteamState.STREAM_STOPPED) {
            return ISSErrors.ISS_SUCCESS;
        } else {
            mAudioTrackSteamState.set(AudioTrackSteamState.STREAM_STOPPED);
            /*
             * 当前合成被该TtsPlayer实例占用时才调用合成stop
             */
            Log.d(TAG, "TTSENGINE stop start------------>");
            if (TTSENGINE.getListener() == this) {
                TTSENGINE.stop();
            }
            Log.d(TAG, "TTSENGINE stop end------------>");
            boolean lockResult = false;
            try {
                lockResult = mAudioTrackLock.tryLock(WAIT_LOCK_TIME, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                Log.e(TAG_AUDIOTRACK, "try lock audio track fail,err " + e.getMessage());
            }
            if (!lockResult) {
                int resetResult = resetAudioTrack();
                if (resetResult != 0) {
                    Log.e(TAG_AUDIOTRACK, "reset audio track err");
                    return -1;
                }
                mAudioTrackLock.lock();
            }
            if (mAudioTrack != null) {
                mAudioTrack.pause();
                mAudioTrack.flush();
            }
            mAudioTrackLock.unlock();
            synchronized (mWorkingThreadSyncObj) {
                mWorkingThreadSyncObj.notifyAll();
            }
            if (listener != null) {
                listener.onTTSPlayInterrupted();
            }
            return ISSErrors.ISS_SUCCESS;
        }
    }

    int release() {
        Log.d(TAG, "Release");
        mAudioTrackSteamState.set(AudioTrackSteamState.STREAM_RELEASED);
        // Exit Working Thread
        mAudioWriteWorkingFunc.setExitFlag();
        if (mThreadAudioWrite != null) {
            try {
                mThreadAudioWrite.join();
                mThreadAudioWrite = null;
            } catch (InterruptedException e) {
                e.printStackTrace();
                Thread.currentThread().interrupt();
            }
        }

        int retEngine = TTSENGINE.release();
        if(ISSErrors.ISS_SUCCESS != retEngine){
            return retEngine;
        }

        mAudioTrackLock.lock();
        if (mAudioTrack != null) {
            mAudioTrack.release();
            mAudioTrack = null;
        }
        mAudioTrackLock.unlock();
        return ISSErrors.ISS_SUCCESS;
    }

    private class AudioWriteWorkingFunc implements Runnable {
        private final static String TAG = "AudioWriteWorkingFunc";
        private boolean mExitFlag = false;

        void clearExitFlag() {
            mExitFlag = false;
        }

        void setExitFlag() {
            mExitFlag = true;
            synchronized (mWorkingThreadSyncObj) {
                mWorkingThreadSyncObj.notifyAll();
            }
        }

        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
            Log.d(TAG, "AudioWriteWorkingFunc In.");
            if (mAudioTrack == null || mMinBufferSizeInBytes == 0) {
                Log.e(
                        TAG,
                        "mAudioTrack==null || mNativeHandle.native_point == 0 || mMinBufferSizeInBytes==0, this should never happen.");
            } else {
                byte[] buffer = new byte[mMinBufferSizeInBytes * 4];
                Log.d(TAG, "mBufferOnceSizeInBytes is "
                        + mMinBufferSizeInBytes * 4);
                while (true) {
                    if (mExitFlag) {
                        break;
                    }
                    if (mAudioTrackSteamState.get() == AudioTrackSteamState.STREAM_RUNNING
                            && mOnDataReadyFlag.get()) {
                        int[] buffer_size = new int[1];
                        int err_ret = TTSENGINE.getAudioData(buffer, mMinBufferSizeInBytes * 4, buffer_size);
                        if (err_ret == ISSErrors.ISS_ERROR_TTS_COMPLETED) {
                            Log.d(TAG, "libisstts.getAudioData Completed.");
                            mAudioTrackSteamState.set(AudioTrackSteamState.STREAM_STOPPED);
                            if (listener != null) {
                                mAudioTrackLock.lock();
                                if (mAudioTrack != null) {
                                    Log.d(TtsPlayer.this.TAG, "mAudioTrackLock pause start");
                                    mAudioTrack.pause();
                                    mAudioTrack.flush();
                                    Log.d(TtsPlayer.this.TAG, "mAudioTrackLock pause end");
                                }
                                mAudioTrackLock.unlock();
                                listener.onTTSPlayCompleted();
                            }
                        } else {
                            if (buffer_size[0] > 0) {
                                int ret = 0;
                                mAudioTrackLock.lock();
                                try {
                                    if (mAudioTrack.getPlayState() != AudioTrack.PLAYSTATE_STOPPED) {
                                        ret = mAudioTrack.write(buffer, 0,
                                                buffer_size[0]);
                                    }
                                } catch (Exception e) {
                                    e.printStackTrace();
                                } finally {
                                    if (((ReentrantLock) mAudioTrackLock).isHeldByCurrentThread()) {
                                        mAudioTrackLock.unlock();
                                    }
                                }
                                if (ret < 0) {
                                    Log.e(TAG, "mAudioTrack.write(size="
                                            + buffer_size[0] + ") ret " + ret);
                                    mAudioTrackSteamState.set(AudioTrackSteamState.STREAM_STOPPED);
                                    Thread.yield();//将当前线程变为就绪态，将CPU时间片让给其他线程。
                                } else {
                                    Log.d(TAG, "mAudioTrack.write ok");
                                }
                            } else {
                                // 合成效率不够，这不该发生
                                synchronized (mWorkingThreadSyncObj) {
                                    try {
                                        Log.d(TAG, "Before wait(20)");
                                        mWorkingThreadSyncObj.wait(20);
                                        Log.d(TAG, "After wait(20)");
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                        Thread.currentThread().interrupt();
                                    }
                                }
                            }
                        }
                    } else {
                        synchronized (mWorkingThreadSyncObj) {
                            if (mAudioTrackSteamState.get() != AudioTrackSteamState.STREAM_RUNNING
                                    || !mOnDataReadyFlag.get()) {
                                try {
                                    Log.d(TAG, "Before wait()");
                                    mWorkingThreadSyncObj.wait();
                                    Log.d(TAG, "After wait()");
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                    Thread.currentThread().interrupt();
                                }
                            }
                        }
                    }
                }
            }
            Log.d(TAG, "AudioWriteWorkingFunc Out.");
        }
    }

    @Override
    public void onDataReady() {
        Log.d(TAG, "onDataReady");
        mOnDataReadyFlag.set(true);
        synchronized (mWorkingThreadSyncObj) {
            mWorkingThreadSyncObj.notifyAll();
        }
        Log.d(TAG, "onTTSPlayBegin start");
        if (listener != null) {
            listener.onTTSPlayBegin();
        }
        Log.d(TAG, "onTTSPlayBegin end");
    }

    @Override
    public void onProgress(int nTextIndex, int nTextLen) {
        // 过滤重复的进度信息
        if (nPreTextIndex < nTextIndex) {
            Log.d(TAG, "onProgress(" + nTextIndex + ", " + nTextLen + ")");
            if (listener != null) {
                listener.onTTSProgressReturn(nTextIndex, nTextLen);
            }
        }
        nPreTextIndex = nTextIndex;
    }

    @Override
    public void onInterrupted() {
        if (null != listener) {
            stop();
            listener.onTTSPlayInterrupted();
        }
    }

    /**
     * TTS状态回调类
     */
    public interface ITtsStateListener{
        /**
         * tts开始合成回调
         */
        void onTTSPlayBegin();

        /**
         * tts合成结束回调
         */
        void onTTSPlayCompleted();

        /**
         * tts合成中断回调
         */
        void onTTSPlayInterrupted();

        /**
         * tts合成进度回调
         */
        void onTTSProgressReturn(int nTextIndex, int nTextLen);
    }
}
